package com.gyc.game.cchess.model;

import static com.gyc.game.cchess.model.ChessBoardBitPlanes.B_BISHOP;
import static com.gyc.game.cchess.model.ChessBoardBitPlanes.B_CANON;
import static com.gyc.game.cchess.model.ChessBoardBitPlanes.B_CAR;
import static com.gyc.game.cchess.model.ChessBoardBitPlanes.B_ELEPHANT;
import static com.gyc.game.cchess.model.ChessBoardBitPlanes.B_HORSE;
import static com.gyc.game.cchess.model.ChessBoardBitPlanes.B_KING;
import static com.gyc.game.cchess.model.ChessBoardBitPlanes.B_PAWN;
import static com.gyc.game.cchess.model.ChessBoardBitPlanes.R_BISHOP;
import static com.gyc.game.cchess.model.ChessBoardBitPlanes.R_CANON;
import static com.gyc.game.cchess.model.ChessBoardBitPlanes.R_CAR;
import static com.gyc.game.cchess.model.ChessBoardBitPlanes.R_ELEPHANT;
import static com.gyc.game.cchess.model.ChessBoardBitPlanes.R_HORSE;
import static com.gyc.game.cchess.model.ChessBoardBitPlanes.R_KING;
import static com.gyc.game.cchess.model.ChessBoardBitPlanes.R_PAWN;

import java.util.HashMap;
import java.util.Map;
import java.util.Random;

/**
 * @author guanyc
 * 
 */
public class TranspositionTable {

	/*
	 * public static enum ENTRY_TYPE { exact, lower_bound, upper_bound };
	 */

	public static final int TT_NOT_HIT = 66666;
	public static final byte exact = 0;
	public static final byte lower_bound = 1;
	public static final byte upper_bound = 2;

	public static class HashItem {
		long checksum; // or long long might be even better
		byte entry_type;
		byte depth;
		short eval;
	};

	int m_nHashKey32[][];// 15*90

	long m_ulHashKey64[][];// 15*90

	static byte[] TTN4ChessId = new byte[128];

	Map<Long, HashItem>[] m_ptt;

	long m_HashKey32;

	long m_HashKey64;

	private Random rand = new Random();

	static {
		TTN4ChessId[B_BISHOP] = 5;
		TTN4ChessId[B_CANON] = 4;
		TTN4ChessId[B_CAR] = 2;
		TTN4ChessId[B_ELEPHANT] = 6;
		TTN4ChessId[B_HORSE] = 3;
		TTN4ChessId[B_KING] = 1;
		TTN4ChessId[B_PAWN] = 7;

		TTN4ChessId[R_BISHOP] = 12;
		TTN4ChessId[R_CANON] = 11;
		TTN4ChessId[R_CAR] = 9;
		TTN4ChessId[R_ELEPHANT] = 13;
		TTN4ChessId[R_HORSE] = 10;
		TTN4ChessId[R_KING] = 8;
		TTN4ChessId[R_PAWN] = 14;

	}

	/**
	 * 产生64位随机数 (2^63-1)
	 * 
	 * @return
	 */
	public long rand64() {
		rand.setSeed(System.currentTimeMillis());

		return rand.nextLong() ^ (rand.nextLong() << 15)
				^ (rand.nextLong() << 30) ^ (rand.nextLong() << 45)
				^ (rand.nextLong() << 60);

	}

	/**
	 * 产生32位随机数
	 * 
	 * @return
	 */
	public int rand32() {

		rand.setSeed(System.currentTimeMillis());
		return rand.nextInt() ^ (rand.nextInt() << 15) ^ (rand.nextInt() << 30);

	}

	public TranspositionTable() {

		m_nHashKey32 = new int[15][90];
		m_ulHashKey64 = new long[15][90];

		m_ptt = new HashMap[2];
		m_ptt[0] = new HashMap<Long, HashItem>(768 * 768);
		m_ptt[1] = new HashMap<Long, HashItem>(768 * 768);

		InitializeHashKey();
	}

	static byte getTTN4ChessId(final byte chessNumber) {
		return TTN4ChessId[chessNumber];

	}

	/**
	 * 生成随机数组
	 * 
	 */
	public void InitializeHashKey() {

		m_ptt[0].clear();
		m_ptt[1].clear();

		rand.setSeed(System.currentTimeMillis());

		for (int i = 0; i < 15; i++) {
			for (byte xy = 0; xy < BitPlane.MAX; xy++) {
				m_nHashKey32[i][xy] = rand.nextInt() ^ (rand.nextInt() << 15)
						^ (rand.nextInt() << 30);
				m_ulHashKey64[i][xy] = rand.nextLong()
						^ (rand.nextLong() << 15) ^ (rand.nextLong() << 30)
						^ (rand.nextLong() << 45) ^ (rand.nextLong() << 60);
			}
		}

		// m_pTT[0] = new HashItem[512 * 512];
		// m_pTT[1] = new HashItem[512 * 512];
	}

	/**
	 * 根据棋盘计算棋盘当前状态下32位及64位Hash值
	 * 
	 * @param CurPosition
	 */
	public void CalculateInitHashKey(ChessBoardBitPlanes CurPosition)// 10*9
	{
		m_HashKey32 = 0;
		m_HashKey64 = 0;

		for (byte xy = 0; xy < BitPlane.MAX; xy++) {
			byte nChessType = CurPosition.getXY(xy);

			if (nChessType != ChessBoardBitPlanes.NOCHESS) {
				m_HashKey32 ^= m_nHashKey32[TTN4ChessId[nChessType]][xy];
				m_HashKey64 ^= m_ulHashKey64[TTN4ChessId[nChessType]][xy];
			}

		}
	}

	public void Hash_MakeMove(ChessMove move)// 10*9
	{

		final byte xfChessId = TTN4ChessId[move.fChessId];

		m_HashKey32 ^= m_nHashKey32[xfChessId][move.fRowColumn];
		m_HashKey64 ^= m_ulHashKey64[xfChessId][move.fRowColumn];

		if (move.tChessId != ChessBoardBitPlanes.NOCHESS) {
			final byte ntoidx = TTN4ChessId[move.tChessId];
			m_HashKey32 ^= m_nHashKey32[ntoidx][move.tRowColumn];
			m_HashKey64 ^= m_ulHashKey64[ntoidx][move.tRowColumn];
		}

		m_HashKey32 ^= m_nHashKey32[xfChessId][move.tRowColumn];
		m_HashKey64 ^= m_ulHashKey64[xfChessId][move.tRowColumn];

	}

	public void Hash_UnMakeMove(ChessMove move)// 10*9
	{
		final byte xfChessId = TTN4ChessId[move.fChessId];
		final byte xtChessId = TTN4ChessId[move.tChessId];

		m_HashKey32 ^= m_nHashKey32[xfChessId][move.tRowColumn];
		m_HashKey64 ^= m_ulHashKey64[xfChessId][move.tRowColumn];

		if (move.tChessId != ChessBoardBitPlanes.NOCHESS) {
			m_HashKey32 ^= m_nHashKey32[xtChessId][move.tRowColumn];
			m_HashKey64 ^= m_ulHashKey64[xtChessId][move.tRowColumn];
		}

		m_HashKey32 ^= m_nHashKey32[xfChessId][move.fRowColumn];
		m_HashKey64 ^= m_ulHashKey64[xfChessId][move.fRowColumn];

	}

	public int LookUpHashTable(int alpha, int beta, int depth, int TableNo) {

		// Log.i("TranspositionTable-LookUpHashTable", "" + m_HashKey32);

		long x = m_HashKey32 & 0xFFFFF; // 10 0000 0000 = 512

		HashItem pht = (HashItem) this.m_ptt[TableNo].get(x);

		if (pht != null) {
			if (pht.depth <= depth && pht.checksum == m_HashKey64) {
				switch (pht.entry_type) {
				case exact:
					return pht.eval;
				case lower_bound:
					if (pht.eval >= beta) {
						return pht.eval;
					}
					break;
				case upper_bound:
					if (pht.eval <= alpha) {
						return pht.eval;
					}
					break;
				}
			}
		}

		return TranspositionTable.TT_NOT_HIT;
	}

	public void EnterHashTable(byte entry_type, int eval, int depth, int TableNo) {

		// Log.i("TranspositionTable-EnterHashTable", "" + m_HashKey32);

		long x = m_HashKey32 & 0xFFFFF;// 二十位哈希地址

		HashItem pht = new HashItem();
		pht.checksum = m_HashKey64;
		pht.entry_type = entry_type;
		pht.eval = (short) eval;
		pht.depth = (byte) depth;

		this.m_ptt[TableNo].put(x, pht);

	}

	public void destroy() {
		try {
			m_nHashKey32 = null;
			m_ulHashKey64 = null;

			m_ptt[0].clear();
			m_ptt[1].clear();
			m_ptt = null;

		} catch (Exception e) {
			// TODO: handle exception
		}

	}

}
